package com.yc.framework.aspectj;


import com.alibaba.fastjson2.JSON;
import com.yc.common.annotation.Log;
import com.yc.system.domain.SysLog;
import com.yc.common.core.domain.entity.SysUser;
import com.yc.common.core.domain.model.LoginUser;
import com.yc.framework.web.service.TokenService;
import com.yc.system.service.ISysLogService;
import com.yc.common.utils.ip.AddressUtil;
import com.yc.common.utils.StringUtil;
import com.yc.common.utils.WebUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Date;

@Aspect
@Component
public class LogAspect {

    @Resource
    private ISysLogService sysLogInfoService;

    @Autowired
    private TokenService tokenService;

    // 配置切入点 - 针对Controller层
    @Pointcut("execution(* com.yc.web.controller.*.*(..))")
    public void controllerPointCut() {
    }

    // 配置切入点 - 针对Service层
    @Pointcut("execution(* com.yc.system.service.*.*(..))")
    public void servicePointCut() {
    }

    // 配置切入点 - 针对有@Log注解的方法
    @Pointcut("@annotation(com.yc.common.annotation.Log)")
    public void logPointCut() {
    }

    /**
     * 环绕通知 - 针对有@Log注解的方法
     */
    @Around("logPointCut()")
    public Object aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        Object result;
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            // 记录异常日志
            handleLogException(joinPoint, e, null);
            throw e;
        }

        long endTime = System.currentTimeMillis();
        long costTime = endTime - startTime;

        // 记录正常日志
        handleLog(joinPoint, result, costTime);

        return result;
    }

    /**
     * 环绕通知 - 针对Controller层
     */
    @Around("controllerPointCut() && !logPointCut()")
    public Object aroundController(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        Object result;
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            // 记录异常日志
            handleLogException(joinPoint, e, "Controller");
            throw e;
        }

        long endTime = System.currentTimeMillis();
        long costTime = endTime - startTime;

        // 超过一定时间的请求记录日志
        if (costTime > 500) {
            handleLog(joinPoint, result, costTime, "Controller");
        }

        return result;
    }

    /**
     * 环绕通知 - 针对Service层
     */
    @Around("servicePointCut() && !logPointCut()")
    public Object aroundService(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        Object result;
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            // 记录异常日志
            handleLogException(joinPoint, e, "Service");
            throw e;
        }

        long endTime = System.currentTimeMillis();
        long costTime = endTime - startTime;

        // 超过一定时间的服务调用记录日志
        if (costTime > 300) {
            handleLog(joinPoint, result, costTime, "Service");
        }

        return result;
    }

    /**
     * 异常通知
     */
    @AfterThrowing(pointcut = "controllerPointCut() || servicePointCut()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Throwable e) {
        handleLogException((ProceedingJoinPoint) joinPoint, e, null);
    }

    /**
     * 处理日志
     */
    private void handleLog(ProceedingJoinPoint joinPoint, Object result, long costTime) {
        handleLog(joinPoint, result, costTime, null);
    }

    /**
     * 处理日志
     */
    private void handleLog(ProceedingJoinPoint joinPoint, Object result, long costTime, String defaultModule) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log logAnnotation = method.getAnnotation(Log.class);

        SysLog sysLog = new SysLog();
        sysLog.setCostTime(costTime);
        sysLog.setCreateTime(new Date());

        // 获取请求相关信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            sysLog.setRequestMethod(request.getMethod());
            sysLog.setRequestUrl(WebUtil.getRequestUrl());
            String ip = WebUtil.getIpAddr();
            sysLog.setIp(ip);
            //添加位置信息
            String realAddress = AddressUtil.getRealAddressByIP(ip);
            sysLog.setLocation(realAddress);
        }

        // 获取当前用户信息
        LoginUser loginUser = tokenService.getLoginUser();
        if (loginUser != null) {
            SysUser user = loginUser.getUser();
            sysLog.setUserId(user.getId());
            sysLog.setUsername(user.getUserName());
        }

        // 设置方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = method.getName();
        sysLog.setMethod(className + "." + methodName + "()");

        // 从注解中获取操作信息
        if (logAnnotation != null) {
            sysLog.setModule(logAnnotation.module());
            sysLog.setType(logAnnotation.type().getCode());
            sysLog.setDescription(logAnnotation.description());

            // 获取请求参数
            if (logAnnotation.saveRequestParams()) {
                sysLog.setRequestParams(getRequestParams(joinPoint));
            }

            // 获取返回结果
            if (logAnnotation.saveResponse() && result != null) {
                String resultStr = JSON.toJSONString(result);
                if (resultStr.length() > 2000) {
                    resultStr = resultStr.substring(0, 2000) + "...";
                }
                sysLog.setResult(resultStr);
            }
        } else if (defaultModule != null) {
            // 没有注解但指定了默认模块
            sysLog.setModule(defaultModule);
            sysLog.setType(4); // 查询
            sysLog.setDescription(methodName);

            // 获取请求参数
            sysLog.setRequestParams(getRequestParams(joinPoint));

            // 获取返回结果
            if (result != null) {
                String resultStr = JSON.toJSONString(result);
                if (resultStr.length() > 2000) {
                    resultStr = resultStr.substring(0, 2000) + "...";
                }
                sysLog.setResult(resultStr);
            }
        }

        sysLog.setStatus(0); // 正常状态

        // 异步保存日志
        sysLogInfoService.insertSysLogInfo(sysLog);
    }

    /**
     * 处理异常日志
     */
    private void handleLogException(ProceedingJoinPoint joinPoint, Throwable e, String defaultModule) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log logAnnotation = method.getAnnotation(Log.class);

        SysLog sysLog = new SysLog();
        sysLog.setCreateTime(new Date());

        // 获取请求相关信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            sysLog.setRequestMethod(request.getMethod());
            sysLog.setRequestUrl(WebUtil.getRequestUrl());
            String ip = WebUtil.getIpAddr();
            sysLog.setIp(ip);

            // 添加位置信息
            sysLog.setLocation(AddressUtil.getRealAddressByIP(ip));
        }

        // 获取当前用户信息，即使在异常场景下也尝试获取
        LoginUser loginUser = tokenService.getLoginUser();
        if (loginUser != null) {
            SysUser user = loginUser.getUser();
            sysLog.setUserId(user.getId());
            sysLog.setUsername(user.getUserName());
        }

        // 从注解中获取操作信息
        if (logAnnotation != null) {
            sysLog.setModule(logAnnotation.module());
            sysLog.setType(logAnnotation.type().getCode());

            // 限制描述长度不超过250个字符
            String description = logAnnotation.description();
            if (description.length() > 250) {
                description = description.substring(0, 250);
            }
            sysLog.setDescription(description);

        } else if (defaultModule != null) {
            // 没有注解但指定了默认模块
            sysLog.setModule(defaultModule);
            sysLog.setType(0); // 其它

            // 限制方法名长度
            String methodName = method.getName();
            if (methodName.length() > 250) {
                methodName = methodName.substring(0, 250);
            }
            sysLog.setDescription(methodName);
        }

        // 记录异常信息
        sysLog.setStatus(1); // 异常状态
        sysLog.setErrorMsg(StringUtil.substring(e.getMessage(), 0, 2000));

        // 异步保存日志
        sysLogInfoService.insertSysLogInfo(sysLog);
    }

    /**
     * 获取请求参数
     */
    private String getRequestParams(JoinPoint joinPoint) {
        String params = "";
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0) {
            try {
                // 过滤特殊类型参数
                Object[] filteredArgs = new Object[args.length];
                for (int i = 0; i < args.length; i++) {
                    if (args[i] instanceof HttpServletRequest
                            || args[i] instanceof HttpServletResponse
                            || args[i] instanceof MultipartFile
                            || args[i] instanceof MultipartFile[]) {
                        filteredArgs[i] = "[不可序列化类型]";
                    } else {
                        filteredArgs[i] = args[i];
                    }
                }

                // 将参数转换为JSON字符串
                params = JSON.toJSONString(filteredArgs);

                // 参数太长时截断
                if (params.length() > 2000) {
                    params = params.substring(0, 2000) + "...";
                }
            } catch (Exception e) {
                params = "[参数序列化异常]";
            }
        }
        return params;
    }
}